package crons

import (
	"bytes"
	"context"
	"errors"
	"github.com/spf13/viper"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/response"
	"go.didapinche.com/foundation/plat-echo/app/ops/repositories"
	"go.didapinche.com/foundation/plat-echo/app/ops/services"
	"go.didapinche.com/foundation/plat-echo/pkg/dingding"
	data2 "go.didapinche.com/foundation/plat-echo/pkg/utils/data"
	time2 "go.didapinche.com/time"
	"go.uber.org/zap"
	"strconv"
	"strings"
	"text/template"
	"time"
)

const NoticeTitle = "持续时间大于5min的告警"

const NoticeMessage = `
{{$alerts := .alerts}}
#### 持续时间大于5min的告警
---
{{range $_,$alert := $alerts}}
##### 告警规则：*{{ $alert.name }}*
{{ if ne $alert.app "" }}##### 应用：*{{ $alert.app }}* {{end}}
{{ if ne $alert.api "" }}##### 接口：*{{ $alert.api }}* {{end}}
{{ if ne $alert.host "" }}##### 主机：*{{ $alert.host }}* {{end}}
##### 应用级别：*L{{ $alert.level }}*
##### 告警级别：*{{ $alert.alertLevel }}*
##### 认领状态：*{{ $alert.claim }}*
##### 告警时间：*{{ $alert.startAlertTime }}*
##### 持续时间：*{{ $alert.duration }}*
##### Value：{{ $alert.value }}
##### 负责人：{{range $_,$owner := $alert.owners}}[@{{$owner}}](){{end}}
> [查看详情]({{ $alert.urlDetail }}) [认领]({{ $alert.urlAlert }})
---
{{end}}
`

type AlertNoticeJob struct {
	logger          *zap.Logger
	alertService    services.AlertCurrentService
	dingDingRepo    repositories.DingdingRepository
	graphRepo       repositories.GraphRepository
	dingDingService dingding.DingDingService
	echoURL         string
	timeout         float64
}

func NewAlertNoticeJob(logger *zap.Logger, alertService services.AlertCurrentService, dingDingRepo repositories.DingdingRepository,
	v *viper.Viper, dingDingService dingding.DingDingService, graphRepo repositories.GraphRepository) *AlertNoticeJob {
	job := &AlertNoticeJob{
		logger:          logger,
		alertService:    alertService,
		dingDingRepo:    dingDingRepo,
		dingDingService: dingDingService,
		graphRepo:       graphRepo,
		echoURL:         v.GetString("echo.ruleurl"),
		timeout:         v.GetFloat64("alert.notice.timeout"),
	}
	if job.timeout == 0 {
		job.timeout = 5
	}
	return job
}

func (a *AlertNoticeJob) Run() {
	a.logger.Info("alert notice job start")

	ctx := context.Background()
	//目标钉钉群
	dings, _, err := a.dingDingRepo.Find(ctx, &models.DingDing{Name: "Echo-SLA"})
	if err != nil {
		a.logger.Error("call dingDingRepo.Find() error", zap.Error(err))
		return
	}

	if len(dings) == 0 {
		a.logger.Error("钉钉群 Echo-SLA 不存在", zap.Error(errors.New("钉钉群 Echo-SLA 不存在")))
		return
	}

	alerts, err := a.alertService.FindByDate(ctx, time2.Time{time.Now().Add(-5 * time.Minute)})
	if err != nil {
		a.logger.Error("alert_notice_job run error", zap.Error(err))
		return
	}
	alertMap := make(map[string][]*response.AlertRecordResp)
	for _, alert := range alerts {
		lasttime := time.Now().Sub(alert.CreateTime.Time)

		if alert.AlertLevel == 1 {
			if lasttime >= viper.GetDuration("warning.time") {
				if _, ok := alertMap[alert.Token]; ok {
					alertMap[alert.Token] = append(alertMap[alert.Token], alert)
				} else {
					alerts2 := make([]*response.AlertRecordResp, 0)
					alerts2 = append(alerts2, alert)
					alertMap[alert.Token] = alerts2
				}
				continue
			}
		} else if alert.AlertLevel == 0 {
			updateLevel := viper.GetString("alert.update.level")
			levels := strings.Split(updateLevel, ",")
			if lasttime >= viper.GetDuration("critical.time1") && data2.ContainString(levels, strconv.Itoa(alert.Level)) {
				token := dings[0].Token
				if _, ok := alertMap[token]; ok {
					alertMap[token] = append(alertMap[token], alert)
				} else {
					alerts2 := make([]*response.AlertRecordResp, 0)
					alerts2 = append(alerts2, alert)
					alertMap[token] = alerts2
				}
				continue
			}
			if lasttime >= viper.GetDuration("critical.time2") {
				if _, ok := alertMap[alert.Token]; ok {
					alertMap[alert.Token] = append(alertMap[alert.Token], alert)
				} else {
					alerts2 := make([]*response.AlertRecordResp, 0)
					alerts2 = append(alerts2, alert)
					alertMap[alert.Token] = alerts2
				}
			}
		}
	}
	for k, v := range alertMap {

		var alertTmp = make([]map[string]interface{}, 0)
		mobiles := make([]string, 0)

		for _, alert := range v {
			value := models.FormatValue(alert.Value, alert.Format)
			if value == "" {
				value = alert.Value
			}
			alert.Value = value
			if alert.AlertLevel == 0 {
				alert.AlertLevelName = "严重"
			} else if alert.AlertLevel == 1 {
				alert.AlertLevelName = "警告"
			} else if alert.AlertLevel == 2 {
				alert.AlertLevelName = "忽略"
			}
			if alert.ClaimStatus == 0 {
				alert.ClaimStatusString = "未认领"
			} else {
				alert.ClaimStatusString = "已认领"
				if alert.ClaimUser != nil {
					alert.ClaimStatusString += "(认领人:" + alert.ClaimUser.UserName + ")"
				}
			}
			alert.UrlAlert = alert.UrlDetail + "&auto_claim=true"
			alert.Duration = time.Now().Sub(alert.CreateTime.Time).String()
			tmpMap := map[string]interface{}{
				"name":           alert.Name,
				"app":            alert.AppName,
				"api":            alert.ApiName,
				"host":           alert.Host,
				"level":          alert.Level - 1,
				"startAlertTime": alert.CreateTime,
				"duration":       alert.Duration,
				"value":          alert.Value,
				"alertLevel":     alert.AlertLevelName,
				"urlDetail":      alert.UrlDetail,
				"urlAlert":       alert.UrlAlert,
				"owners":         alert.Owners,
				"claim":          alert.ClaimStatusString,
			}
			alertTmp = append(alertTmp, tmpMap)
		}

		t, err := template.New("temp").Parse(NoticeMessage)
		if err != nil {
			a.logger.Error("parse message template error", zap.Error(err))
			return
		}
		if len(alertTmp) == 0 {
			return
		}
		var buf bytes.Buffer
		err = t.Execute(&buf, map[string]interface{}{
			"alerts": alertTmp,
		})

		msg := &dingding.DingMessage{
			MessageType: "markdown",
			Markdown: dingding.Markdown{
				Title: NoticeTitle,
				Text:  buf.String(),
			},
			At: dingding.At{
				IsAtAll: false,
			},
		}

		msg.At.AtMobiles = mobiles

		a.dingDingService.SendMessage(k, msg)

	}
	a.logger.Info("alert notice job end")
}

func (a *AlertNoticeJob) Cron() string {
	return "0 0/5 * * * ?"
}

func (a *AlertNoticeJob) Name() string {
	return "alert_notice_job"
}
